// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include <melon/range.h>

#include <pollux/type/type_parser.h>

namespace kumo::pollux::type::hive {
    // TODO : Find out what to do with these types
    // NUMERIC, INTERVAL, VARCHAR, VOID
    enum class TokenType {
        Boolean,
        Byte,
        Short,
        Integer,
        Date,
        Long,
        Float,
        Double,
        String,
        Binary,
        Timestamp,
        Opaque,
        List,
        Map,
        Struct,
        StartSubType,
        EndSubType,
        Colon,
        Comma,
        Number,
        Identifier,
        EndOfStream,
        Decimal,
        LeftRoundBracket,
        RightRoundBracket,
        MaxTokenType
    };

    struct TokenMetadata {
        TokenType tokenType;
        pollux::TypeKind typeKind;
        std::vector<std::string> tokenString;
        bool isPrimitiveType;

        TokenMetadata(
            TokenType typ,
            pollux::TypeKind kind,
            std::vector<std::string> &&ts,
            bool ip)
            : tokenType(typ),
              typeKind(kind),
              tokenString(std::move(ts)),
              isPrimitiveType(ip) {
        }
    };

    struct Token {
        TokenMetadata *metadata;
        melon::StringPiece value;

        TokenType tokenType() const;

        pollux::TypeKind type_kind() const;

        bool isPrimitiveType() const;

        bool isValidType() const;

        bool isEOS() const;

        bool isOpaqueType() const;
    };

    struct TokenAndRemaining : public Token {
        melon::StringPiece remaining;
    };

    struct Result {
        std::shared_ptr<const pollux::Type> type;
    };

    struct ResultList {
        std::vector<std::shared_ptr<const pollux::Type> > typelist;
        std::vector<std::string> names;
    };

    class HiveTypeParser : public type::TypeParser {
    public:
        HiveTypeParser();

        ~HiveTypeParser() override = default;

        std::shared_ptr<const pollux::Type> parse(const std::string &ser) override;

    private:
        int8_t makeTokenId(TokenType tokenType) const;

        Result parseType();

        ResultList parseTypeList(bool hasFieldNames);

        TokenType lookAhead() const;

        Token eatToken(TokenType tokenType, bool ignorePredefined = false);

        Token nextToken(bool ignorePredefined = false);

        TokenAndRemaining nextToken(
            melon::StringPiece sp,
            bool ignorePredefined = false) const;

        TokenAndRemaining makeExtendedToken(
            TokenMetadata *tokenMetadata,
            melon::StringPiece sp,
            size_t len) const;

        template<TokenType KIND, pollux::TypeKind TYPEKIND>
        void setupMetadata(const char *tok = "") {
            setupMetadata<KIND, TYPEKIND>(std::vector<std::string>{std::string{tok}});
        }

        template<TokenType KIND, pollux::TypeKind TYPEKIND>
        void setupMetadata(std::vector<std::string> &&tokens) {
            static constexpr bool isPrimitive =
                    pollux::TypeTraits<TYPEKIND>::isPrimitiveType;
            metadata_[makeTokenId(KIND)] = std::make_unique<TokenMetadata>(
                KIND, TYPEKIND, std::move(tokens), isPrimitive);
        }

        TokenMetadata *getMetadata(TokenType type) const;

    private:
        std::vector<std::unique_ptr<TokenMetadata> > metadata_;
        melon::StringPiece remaining_;
    };
} // namespace kumo::pollux::type::hive
